package view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.ImageIcon;
import javax.swing.JToolBar;
import javax.swing.JButton;
import javax.swing.JScrollPane;
import control.ControllerWindow;

import javax.swing.JSplitPane;

import model.CompileError;
import model.ParseError;
import model.SemanticError;
import model.Token;

/**
 * Classe que contém todas informações e ações inerentes à janela de
 * interface com o usuário.
 * 
 * @author 	Jody Matos, Alenilson
 * @version 1.4
 * @since	10/18/2011
 * 
 * <dt><b>History:</b></dt>
 * 	<dd>1.0: Creation of first methods</dd>
 *  <dd>1.1: Update of overall methods adapting him to our project instead auto-generated 
 *  	code)</dd>
 *  <dd>1.2: Changing Full Path to Relative Path of Icons</dd>
 *  <dd>1.3: Reading  a File and showing it on the main window</dd>
 *  <dd>1.4: Changing Error and Token's<code>JTextArea</code> to <code>JTable</code>,
 *  	showing results</dd>
 *  
 */
@SuppressWarnings("serial")
public class ViewWindow extends JFrame{
	private JSplitPane 	horizontalSplit;
	private JSplitPane 	verticalSplit;
	private JScrollPane sourceScroll;
	private JScrollPane errorsScroll;
	private JScrollPane tokenScroll;
	private JTextArea 	sourceText;
	private JTextArea	errorsArea;
	private JTable 		tokenTable;
	private TableModelToken tokenModel;
	//private TableModelLexicError errorModel;
	private JToolBar 	toolBar;
	private ImageIcon 	openIcon;
	private ImageIcon 	compileIcon;
	private JButton 	openButton;
	private JButton 	compileButton;
	private JMenuItem	openMenu;
	private JMenuItem	exitMenu;
	private int 		width;
	private int 		height;
	private int 		inset;

	/**
	 * Cria uma instância de <code>ViewWindow</code>, instanciando
	 * e posicionando cada um componentes visuais da janela, além de
	 * indicar os devidos controladores para os eventos tratados.
	 * 
	 * @param controller <code>ControllerWindow</code> com o controlador
	 * 					 dos a serem tratados 
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public ViewWindow(ControllerWindow controller) {
		super("[2011.2] EXA815 - Ling. de Prog. (TP01 - Alenilson e Jody)");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		inset = 15;

		toolBar = new JToolBar();
		openIcon = new ImageIcon("open_24.png");
		compileIcon = new ImageIcon("play_24.png");

		openButton = new JButton(openIcon);
		openButton.setToolTipText("Open");
		openButton.addActionListener(controller);

		compileButton = new JButton(compileIcon);
		compileButton.setToolTipText("Compile");
		compileButton.addActionListener(controller);

		toolBar.add(openButton);
		toolBar.add(compileButton);

		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		width = screenSize.width - inset * 2;
		height = screenSize.height - inset * 2;

		tokenModel = new TableModelToken(new LinkedList());

		tokenTable = new JTable();
		tokenTable.setModel(tokenModel);

		//errorModel = new TableModelLexicError(new LinkedList());

		errorsArea = new JTextArea();
		//errorsArea.setModel(errorModel);

		sourceText = new JTextArea("");
		TextLineNumber lineNumber = new TextLineNumber(sourceText);		

		tokenTable.setBorder(BorderFactory.createLoweredBevelBorder());
		errorsArea.setBorder(BorderFactory.createLoweredBevelBorder());
		sourceText.setBorder(BorderFactory.createLoweredBevelBorder());

		tokenScroll = new JScrollPane(tokenTable);
		errorsScroll = new JScrollPane(errorsArea);
		sourceScroll = new JScrollPane(sourceText);

		sourceScroll.setRowHeaderView(lineNumber);

		tokenScroll.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Tokens"));
		errorsScroll.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Errors"));
		sourceScroll.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Source Code"));

		tokenTable.setMinimumSize(new Dimension(10, 10));
		errorsArea.setMinimumSize(new Dimension(10, 10));
		sourceText.setMinimumSize(new Dimension(10, 10));

		horizontalSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, tokenScroll, sourceScroll);
		horizontalSplit.setOneTouchExpandable(true);
		horizontalSplit.setDividerLocation(width/5);
		horizontalSplit.setPreferredSize(new Dimension(width, height*3/4));

		verticalSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, horizontalSplit, errorsScroll);
		verticalSplit.setOneTouchExpandable(true);
		verticalSplit.setDividerLocation(height*3/5);
		verticalSplit.setPreferredSize(new Dimension(width, height/4));

		this.add(verticalSplit);
		this.add(this.getToolBar(), BorderLayout.NORTH);
		this.setPreferredSize(new Dimension(width, height));
		this.pack();
		this.setJMenuBar(this.createMenuBar(controller));
	}

	/**
	 * Modulariza o processo de instanciamento do menu principal
	 * da janela.
	 * 
	 * @param controller <code>ControllerWindow</code> com o controlador
	 * 					 dos a serem tratados 
	 * @return <code>JMenuBar</code> com o menu contruído
	 */
	private JMenuBar createMenuBar(ControllerWindow controller) {
		JMenuBar menuBar = new JMenuBar();
		JMenu menu = new JMenu("File");
		menu.setMnemonic(KeyEvent.VK_F);
		openMenu = new JMenuItem("Open...");
		openMenu.setMnemonic(KeyEvent.VK_O);
		openMenu.addActionListener(controller);

		exitMenu = new JMenuItem("Exit");
		exitMenu.setMnemonic(KeyEvent.VK_E);
		exitMenu.addActionListener(controller);

		menu.add(openMenu);
		menu.add(exitMenu);
		menuBar.add(menu);
		return menuBar;
	}

	private JToolBar getToolBar(){
		return this.toolBar;
	}

	public JButton getOpenButton(){
		return this.openButton;
	}

	public JButton getCompileButton(){
		return this.compileButton;
	}

	public JMenuItem getOpenMenu(){
		return this.openMenu;
	}

	public JMenuItem getExitMenu(){
		return this.exitMenu;
	}

	/**
	 * Exibe num <code>JTextArea</code> o conteúudo do arquivo 
	 * do código-fonte escolhido élo usuário. 
	 * 
	 * @param source <code>String</code> com o conteúdo do 
	 * 				 código-fonte.
	 */
	public void setSourceCode(String source) {
		this.sourceText.setText(source);
		this.sourceText.setEditable(false);
		this.repaintFrame();
	}

	/**
	 * Exibe num <code>JTable</code> as informações dos erros
	 * léxicos encontrados por <code>ControllerAnalyzer</code>.
	 * 
	 * @param errors <code>List</code> com uma lista
	 * 				 de objetos do tipo <code>LexicError</code>
	 */
	public void setErrors(List<CompileError> errors) {
		errorsArea.setText("");
		errorsArea.setFont(new Font("Times New Roman", Font.BOLD, 12));
		if(errors.isEmpty()){
			errorsArea.setForeground(Color.BLACK);
			errorsArea.setText("Ok! Construção realizada com sucesso.");
		}
		/*errorModel = new TableModelLexicError(errors);
		errorsArea.setModel(errorModel);*/
		else {
			errorsArea.setForeground(Color.RED);
			for(CompileError error: errors){
				if(error.getClass()==ParseError.class){
					ParseError parseError = (ParseError) error;
					errorsArea.append("Erro no símbolo '"+parseError.getLexeme()+"', linha "+
							error.getLineNumber()+": "+error.getType()/*+". esperado: "+
							parseError.getExpected()*/+'\n');
				}
				else if(error.getClass()==SemanticError.class){
					SemanticError semError = (SemanticError) error;
					switch (semError.getType()) {
					case TYPE_MISMATCH:
						errorsArea.append("tipos diferentes na linha "+error.getLineNumber()+": "+
								"identificador tipo "+semError.getTypeOccurred()+", enquanto esperado" +
								" era "+semError.getTypeExpected()+'\n');
						break;

					case IDENTIFIER_MISMATCH:
						errorsArea.append("uso de identificador não declarado na linha "+
								error.getLineNumber()+" ("+error.getLexeme()+")"+'\n');
						break;
						
					case IDENTIFIER_REDECLARED:
						errorsArea.append("identificador '"+error.getLexeme()+
							"' sendo redeclarado na linha "+error.getLineNumber()+'\n');
						break;
					}
				} else {
					errorsArea.append("Erro no símbolo '"+error.getLexeme()+"', linha "+
							error.getLineNumber()+": "+error.getType()+'\n');
				}
			}}
		repaintFrame();
	}

	/**
	 * Exibe num <code>JTable</code> as informações dos tokens
	 * identificados por <code>ControllerAnalyzer</code>.
	 * 
	 * @param tokens <code>List</code> com uma lista
	 * 				 de objetos do tipo <code>Token</code>
	 */
	public void setTokens(List<Token> tokens) {
		tokenModel= new TableModelToken(tokens);
		tokenTable.setModel(tokenModel);
		repaintFrame();
	}

	public void repaintFrame() {
		this.validate();
		this.repaint();
	}

}